package flexo.graphic;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.imageio.ImageIO;

import org.lwjgl.*;

import flexo.file.*;

public class GeometryLoader
{
	public static Geometry createQuad(float width, float height)
	{
		flexo.Kernel.log("flexo.graphic.GeometryLoader", "Generating quad mesh");
		
		FloatBuffer vertices = BufferUtils.createFloatBuffer(48); // 12bytes * 4 vertices
		IntBuffer indices = BufferUtils.createIntBuffer(6);
		int numIndices = 0;
		
		float[] vertex = new float[12]; // Each vertex holds 12 bytes
		
		float halfW = width/2.0f;
		float halfH = height/2.0f;
		
		// Upper left
		vertex[0] = -halfW; vertex[1] = halfH; vertex[2] = 0.0f;	// Position
		vertex[3] = 0.0f; vertex[4] = 0.0f; vertex[5] = 1.0f;	// Normal
		vertex[6] = 1.0f; vertex[7] = 1.0f; vertex[8] = 1.0f; vertex[9] = 1.0f;	// RGBA
		vertex[10] = 0.0f; vertex[11] = 0.0f;	// TexCoord
		vertices.put(vertex);
		
		// Lower left
		vertex[0] = -halfW; vertex[1] = -halfH; vertex[2] = 0.0f;	// Position
		vertex[10] = 0.0f; vertex[11] = 1.0f;	// TexCoord
		vertices.put(vertex);
		
		// Lower right
		vertex[0] = halfW; vertex[1] = -halfH; vertex[2] = 0.0f;	// Position
		vertex[10] = 1.0f; vertex[11] = 1.0f;	// TexCoord
		vertices.put(vertex);
		
		// Upper right
		vertex[0] = halfW; vertex[1] = halfH; vertex[2] = 0.0f;	// Position
		vertex[10] = 1.0f; vertex[11] = 0.0f;	// TexCoord
		vertices.put(vertex);
		
		vertices.rewind();
		
		indices.put(0); numIndices++;
		indices.put(1); numIndices++;
		indices.put(2); numIndices++;
		indices.put(0); numIndices++;
		indices.put(2); numIndices++;
		indices.put(3); numIndices++;
		indices.rewind();
		
		return new Geometry(vertices,indices,numIndices);
	}
	
	public static Geometry createTerrain(String heightMap, flexo.math.Coords size, flexo.math.Coords points, float maxHeight)
	{
		flexo.Kernel.log("flexo.graphic.GeometryLoader", "Generating terrain mesh from '"+heightMap+"'");
		int numPrimitives = (int)((points.x - 1) * (points.y - 1))*2;
		int numVertices = (int)(points.x * points.y);
		
		// Initialize the data structures
		FloatBuffer vertices = BufferUtils.createFloatBuffer(12 * numVertices); // 12bytes * 4 vertices
		IntBuffer indices = BufferUtils.createIntBuffer(numPrimitives * 3);		
		
		float[] vertex = new float[12]; // Each vertex holds 12 bytes
		vertex[3] = 0.0f; vertex[4] = 1.0f; vertex[5] = 0.0f;	// Normal
		vertex[6] = 1.0f; vertex[7] = 1.0f; vertex[8] = 1.0f; vertex[9] = 1.0f;	// RGBA
		
		// Load the texture
		BufferedImage bufferedImage = null;
		try
		{
			flexo.file.FileSystem fs = (flexo.file.FileSystem)flexo.Kernel.getSystem(flexo.file.FileSystem.ID);
			bufferedImage = ImageIO.read(new BufferedInputStream(fs.getStream(heightMap)));
		}
		catch (IOException e)
		{			
			e.printStackTrace();
		}
		
		// Espacio entre dos vertices paralelos
		float miniSizeX = size.x / (float)(points.x-1);
		float miniSizeZ = size.y / (float)(points.y-1);
		
		for( int x = 0; x < points.x; ++x )
		{				
			for( int z = 0; z < points.y; ++z )
			{
				int rgba = bufferedImage.getRGB( (int)(((float)x / (float)points.x) * bufferedImage.getWidth()), (int)(((float)z / (float)points.y) * bufferedImage.getHeight()) );
				int red = (rgba >> 16) & 0xff;
				int green = (rgba >> 8) & 0xff;
				int blue = rgba & 0xff;
				
				float vertexHeight = ((red + green + blue )/3.0f/255.0f) * maxHeight;
				
				vertex[0] = (x * miniSizeX) - size.x/2; vertex[1] = vertexHeight; vertex[2] = (z * miniSizeZ) - size.y/2;	// Position
				vertex[10] = (float)x / (float)points.x; vertex[11] = (float)z / (float)points.y;	// TexCoord
				vertices.put(vertex);						
			}
		}			
		vertices.rewind();
		bufferedImage = null;

		// Rellenamos el index buffer
		for (int x = 0; x < points.x - 1; ++x)
		{
			for (int z = 0; z < points.y - 1; ++z)
			{
				indices.put(x * (int)points.x + z);
				indices.put(x * (int)points.x + z + 1);
				indices.put((x + 1) * (int)points.x + z + 1);
				
				indices.put(x * (int)points.x + z);
				indices.put((x + 1) * (int)points.x + z + 1);
				indices.put((x + 1) * (int)points.x + z);
			}
		}
		indices.rewind();
		
		return new Geometry(vertices,indices,numPrimitives * 3);
	}
	
	public static Geometry createObj(String fileName)
	{	
		flexo.Kernel.log("flexo.graphic.GeometryLoader", "Generating mesh from OBJ '"+fileName+"'");
		flexo.file.FileSystem fs = (flexo.file.FileSystem)flexo.Kernel.getSystem(flexo.file.FileSystem.ID);
		File file = fs.createFile(fileName);
		file.load(new flexo.file.reader.Obj());
		
		Node fVert = file.getRootNode().getChild("vertices");
		Node fIndx = file.getRootNode().getChild("indices");
		
		FloatBuffer vertices = BufferUtils.createFloatBuffer(12 * fVert.getChildrenCount()); // 12bytes * 4 vertices
		IntBuffer indices = BufferUtils.createIntBuffer(fIndx.getChildrenCount());
		
		int numIndices = 0;
		
		float[] vertex = new float[12]; // Each vertex holds 12 bytes
		
		// RGBA
		vertex[6] = 1.0f;
		vertex[7] = 1.0f;
		vertex[8] = 1.0f;
		vertex[9] = 1.0f;
		
		for (int vc = 0; vc < fVert.getChildrenCount(); vc++)
		{
			Node currVert = fVert.getChild(vc);
			
			// Position
			vertex[0] = currVert.getProperty("x").getFloatValue();
			vertex[1] = currVert.getProperty("y").getFloatValue();
			vertex[2] = currVert.getProperty("z").getFloatValue();
			
			// Normal
			vertex[3] = currVert.getProperty("nx").getFloatValue();
			vertex[4] = currVert.getProperty("ny").getFloatValue();
			vertex[5] = currVert.getProperty("nz").getFloatValue();
			
			// TexCoord		
			vertex[10] = currVert.getProperty("u").getFloatValue();
			vertex[11] = currVert.getProperty("v").getFloatValue();
			
			// Add the vertex
			vertices.put(vertex);
		}			
		vertices.rewind();
		
		for (numIndices = 0; numIndices < fIndx.getChildrenCount(); numIndices++)
		{
			Node currIndx = fIndx.getChild(numIndices);
			indices.put(currIndx.getProperty(0).getIntValue());
		}
		indices.rewind();
		
		return new Geometry(vertices,indices,numIndices);
	}
}